package bankapplication.com.acme.bankapp.service.bank;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.zip.DataFormatException;

import bankapplication.com.acme.bankapp.domain.bank.*;

public class BankDataLoaderService {
  
  static private String adaptFilePathToCurrentOS(String filepath){
    return filepath.replaceAll("[\\\\/]", System.getProperty("file.separator"));
  }
  
  static private BufferedReader loadFeedFile(String filepath) throws FileNotFoundException {
    return new BufferedReader(new FileReader(adaptFilePathToCurrentOS(filepath)));
  }
  
  static private void parseFeedFileToken(String token, TreeMap<String, String> properties) throws NumberFormatException, 
      DataFormatException{
    int sep_index = token.indexOf('=');
    if(sep_index == -1){
      throw new DataFormatException();
    }
    String property = token.substring(0, sep_index);
    String prop_value = token.substring(sep_index+1, token.length());
    if(!properties.containsKey(property)){
      throw new DataFormatException();
    }
    else{
      properties.put(property, prop_value);
    }
  }
  
  static public Client parseClientFromString(String line) throws DataFormatException{
    TreeMap<String, String> properties = new TreeMap<String, String>();
    properties.put("accounttype", null);
    properties.put("balance", null);
    properties.put("overdraft", null);
    properties.put("name", null);
    properties.put("gender", null);
    StringTokenizer tokenizer = new StringTokenizer(line, ";", false);
    while(tokenizer.hasMoreTokens()){
      try{
        parseFeedFileToken(tokenizer.nextToken(), properties);
      }
      catch(NumberFormatException e){
        throw new DataFormatException(e.getMessage());
      }
    }
    for(Map.Entry<String,String> entry : properties.entrySet()) {
      if(entry.getValue() == null){
        throw new DataFormatException();
      }
    }
    
    Account account;
    switch(properties.get("accounttype")){
    case "s":
      account = new SavingsAccount(Double.parseDouble(properties.get("balance")));
      break;
    case "c":
      account = new CheckingAccount(Double.parseDouble(properties.get("balance")),Double.parseDouble(properties.get("overdraft")));
      break;
    default:
      throw new DataFormatException();
    }
    HashSet<Account> accounts = new HashSet<Account>();
    accounts.add(account);
    Gender gender;
    switch(properties.get("gender")){
    case "m":
      gender = Gender.MALE;
      break;
    case "f":
      gender = Gender.FEMALE;
      break;
    default:
      throw new DataFormatException();
    }
    return new Client(properties.get("name"), gender, accounts);
  }
  
  static private HashSet<Client> parseFeedFile(BufferedReader feedfile) throws IOException, DataFormatException {
    HashSet<Client> clients = new HashSet<Client>();
    try{
      String line;
      while((line = feedfile.readLine())!= null){
        clients.add(parseClientFromString(line));
      }
      return clients;
    }
    finally{
      feedfile.close();
    }
  }
  
  static public Bank loadBankFromFile(String filepath, EmailService emailService) throws DataFormatException {
    Bank bank;
    try{
      bank = new Bank(new HashSet<ClientRegistrationListener>(), parseFeedFile(loadFeedFile(adaptFilePathToCurrentOS(filepath))), emailService);
    }
    catch(IOException e){
      throw new DataFormatException(e.getMessage());
    }
    return bank;
  }

  static public void saveBank(Bank bank) throws DataFormatException {
    String sourcefilepath = "/home/p1/Рабочий стол/bank.source";
    try{
      ObjectOutputStream outputstream = new ObjectOutputStream(new FileOutputStream(adaptFilePathToCurrentOS(sourcefilepath)));
      outputstream.writeObject(bank);
      outputstream.close();
    }
    catch(IOException e){
      throw new DataFormatException(e.getMessage());
    }
  }
  
  static public Bank readBank() throws DataFormatException {
    String sourcefilepath = "/home/p1/Рабочий стол/bank.source";
    Bank bank;
    try{
      ObjectInputStream inputstream = new ObjectInputStream(new FileInputStream(adaptFilePathToCurrentOS(sourcefilepath)));
      bank = (Bank)inputstream.readObject();
      inputstream.close();
    }
    catch(IOException|ClassCastException|ClassNotFoundException e){
      throw new DataFormatException(e.getMessage());
    }
    return bank;
  }

}
